package net.cyue.homework.student.management.http;

import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import net.cyue.homework.student.management.http.annotation.*;
import net.cyue.homework.student.management.util.DataValidator;
import net.cyue.nest.nestjava.common.annotation.Body;
import net.cyue.nest.nestjava.common.annotation.Request;
import net.cyue.nest.nestjava.common.annotation.Response;
import net.cyue.nest.nestjava.common.enums.HTTPStatus;
import net.cyue.nest.nestjava.common.interfaces.http.IHTTPRequest;
import net.cyue.nest.nestjava.common.interfaces.http.IHTTPResponse;
import net.cyue.nest.nestjava.platform.tomcat.TomcatHTTPRequest;
import net.cyue.nest.nestjava.platform.tomcat.TomcatHTTPResponse;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

interface IHTTPServlet {
    void doGet(IHTTPRequest request, IHTTPResponse response) throws IOException;
    void doPost(IHTTPRequest request, IHTTPResponse response) throws IOException;
    void doPut(IHTTPRequest request, IHTTPResponse response) throws IOException;
    void doDelete(IHTTPRequest request, IHTTPResponse response) throws IOException;
}

@FunctionalInterface
interface RequestHandler {
    void handle(IHTTPRequest request, IHTTPResponse response) throws IOException;
}

public abstract class HTTPServlet extends HttpServlet implements IHTTPServlet {

    private final List<RequestHandler> getMethodHandlerList = new ArrayList<>();
    private final List<RequestHandler> postMethodHandlerList = new ArrayList<>();
    private final List<RequestHandler> putMethodHandlerList = new ArrayList<>();
    private final List<RequestHandler> deleteMethodHandlerList = new ArrayList<>();

    private RequestHandler createRequestHandler(Method m) {
        Parameter[] parameters = m.getParameters();

        int requestIndex = -1;
        int responseIndex = -1;
        int bodyIndex = -1;
        Parameter bodyParameter = null;
        Class<?> bodyClz = null;
        for (int i = 0; i < parameters.length; i++) {
            Parameter p = parameters[i];
            Class<?> pClz = p.getType();
            System.out.println("检查 " + p.getName());
            if (pClz.equals(IHTTPRequest.class)) {
                requestIndex = i;
                continue;
            }
            if (pClz.equals(IHTTPResponse.class)) {
                responseIndex = i;
                continue;
            }

            Request requestAnnotation =  p.getAnnotation(Request.class);
            Response responseAnnotation = p.getAnnotation(Response.class);
            Body bodyAnnotation = p.getAnnotation(Body.class);
            if (requestAnnotation != null) {
                requestIndex = i;
            } else if (responseAnnotation != null) {
                responseIndex = i;
            } else if (bodyAnnotation != null) {
                System.out.println(p.getName() + " 是 body");
                bodyIndex = i;
                bodyParameter = p;
                bodyClz = pClz;
            }
        }

        int finalRequestIndex = requestIndex;
        int finalResponseIndex = responseIndex;
        int finalBodyIndex = bodyIndex;
        Parameter finalBodyParameter = bodyParameter;
        Class<?> finalBodyClz = bodyClz;

        Object[] methodParameters = new Object[parameters.length];
        return (request, response) -> {
            System.out.println("调用 handler");
            Arrays.fill(parameters, null);
            if (finalRequestIndex != -1) {
                System.out.println("传递 request");
                methodParameters[finalRequestIndex] = request;
            }
            if (finalResponseIndex != -1) {
                System.out.println("传递 response");
                methodParameters[finalResponseIndex] = response;
            }
            if (finalBodyIndex != -1) {
                System.out.println("传递 body");
                Object body = request.body().toObject(finalBodyClz);
                methodParameters[finalBodyIndex] = body;
                if (finalBodyParameter.getAnnotation(Valid.class) != null) {
                    System.out.println("验证 body");
                    Map<String, Object> errorMap = DataValidator.validate(body);
                    if (errorMap != null) {
                        System.out.println("body 存在参数错误");
                        response.code(HTTPStatus.BAD_REQUEST);
                        response.send(errorMap);
                        return;
                    }
                }
            }
            try {
                m.invoke(this, methodParameters);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        };
    }


    @Override
    public void init(ServletConfig config) {
        Class<?> clz = this.getClass();
        System.out.println("初始化" + clz.getName());
        Method[] methods = clz.getDeclaredMethods();
        for (Method m : methods) {
            if (
                m.getAnnotation(CommonMethod.class) != null ||
                m.getAnnotation(Override.class) != null
            ) {
                continue;
            }

            GetMethod getMethodAnnotation = m.getAnnotation(GetMethod.class);
            PostMethod postMethodAnnotation = m.getAnnotation(PostMethod.class);
            PutMethod putMethodAnnotation = m.getAnnotation(PutMethod.class);
            DeleteMethod deleteMethodAnnotation = m.getAnnotation(DeleteMethod.class);
            if (
                getMethodAnnotation == null &&
                postMethodAnnotation == null &&
                putMethodAnnotation == null &&
                deleteMethodAnnotation == null
            ) {
                continue;
            }

            System.out.println("为 " + m.getName() + " 创建 handler");
            RequestHandler handler = this.createRequestHandler(m);
            if (getMethodAnnotation != null) {
                System.out.println(m.getName() + " GET");
                this.getMethodHandlerList.add(handler);
            }
            if (postMethodAnnotation != null) {
                System.out.println(m.getName() + " POST");
                this.postMethodHandlerList.add(handler);
            }
            if (putMethodAnnotation != null) {
                System.out.println(m.getName() + " PUT");
                this.putMethodHandlerList.add(handler);
            }
            if (deleteMethodAnnotation != null) {
                System.out.println(m.getName() + " DELETE");
                this.deleteMethodHandlerList.add(handler);
            }
        }
    }


    @Override
    protected void service(
        HttpServletRequest request,
        HttpServletResponse response
    ) throws IOException, ServletException {
        System.out.println(this.getClass().getName() + " service");
        response.setContentType("text/html;charset=utf-8");
        super.service(request, response);
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println(this.getClass().getName() + " doGet");
        response.setContentType("text/html;charset=utf-8");
        TomcatHTTPRequest iRequest = new TomcatHTTPRequest(request);
        TomcatHTTPResponse iResponse = new TomcatHTTPResponse(response);
        if (this.getMethodHandlerList.isEmpty()) {
            System.out.println("没有 GET Handler");
            this.doGet(iRequest, iResponse);
        } else {
            for (RequestHandler handler : this.getMethodHandlerList) {
                handler.handle(iRequest, iResponse);
            }
        }
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println(this.getClass().getName() + " doPost");
        response.setContentType("text/html;charset=utf-8");
        TomcatHTTPRequest iRequest = new TomcatHTTPRequest(request);
        TomcatHTTPResponse iResponse = new TomcatHTTPResponse(response);
        if (this.postMethodHandlerList.isEmpty()) {
            System.out.println("没有 POST Handler");
            this.doPost(iRequest, iResponse);
        } else {
            for (RequestHandler handler : this.postMethodHandlerList) {
                handler.handle(iRequest, iResponse);
            }
        }
    }
    @Override
    protected void doPut(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println(this.getClass().getName() + " doPut");
        response.setContentType("text/html;charset=utf-8");
        TomcatHTTPRequest iRequest = new TomcatHTTPRequest(request);
        TomcatHTTPResponse iResponse = new TomcatHTTPResponse(response);
        if (this.putMethodHandlerList.isEmpty()) {
            System.out.println("没有 PUT Handler");
            this.doPut(iRequest, iResponse);
        } else {
            for (RequestHandler handler : this.putMethodHandlerList) {
                handler.handle(iRequest, iResponse);
            }
        }
    }
    @Override
    protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println(this.getClass().getName() + " doDelete");
        response.setContentType("text/html;charset=utf-8");
        TomcatHTTPRequest iRequest = new TomcatHTTPRequest(request);
        TomcatHTTPResponse iResponse = new TomcatHTTPResponse(response);
        if (this.deleteMethodHandlerList.isEmpty()) {
            System.out.println("没有 DELETE Handler");
            this.doDelete(iRequest, iResponse);
        } else {
            for (RequestHandler handler : this.deleteMethodHandlerList) {
                handler.handle(iRequest, iResponse);
            }
        }
    }


    private void notSupportedMethod(
        IHTTPRequest request,
        IHTTPResponse response
    ) throws IOException {
        String msg = "http.method_" + request.method() + "_not_supported";
        response.code(HTTPStatus.METHOD_NOT_ALLOWED);
        response.end(msg);
    }
    public void doGet(
        IHTTPRequest request,
        IHTTPResponse response
    ) throws IOException {
        this.notSupportedMethod(request, response);
    }
    public void doPost(
        IHTTPRequest request,
        IHTTPResponse response
    ) throws IOException {
        this.notSupportedMethod(request, response);
    }
    public void doPut(
        IHTTPRequest request,
        IHTTPResponse response
    ) throws IOException {
        this.notSupportedMethod(request, response);
    }
    public void doDelete(
        IHTTPRequest request,
        IHTTPResponse response
    ) throws IOException {
        this.notSupportedMethod(request, response);
    }
}


